/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.commons.ui;

import java.awt.*;

/**
 * Improved {@link GridLayout} layout manager, useful for form dialogs or
 * other grid-layered panels. Components are layouted as text words on page in order
 * as were added to parent container.
 * <p>Size of pane and computing of component position is based on
 * {@link Component#getPreferredSize()}.
 * <p>Width of every column and height of every row is computed separately and
 * equals to maximum width or height of components on given column or row.
 * <p>Components on every column or row can be aligned using
 * {@link #setColumnsHAlign(#HAlign[])} for
 * horizontal aligment, or {@link #setRowsVAlign(#VAlign[])}
 * method.
 * <p>Overall aligment can be preset by constructor.
 * <p>Example of use (designed for form dialog with two columns - on the left are labels
 * aligned to right, on the right are input fields, aligned (resized) to fill all cell space;
 * rows are aligned vertically to center)<br>
 * <pre>
        LeaveGridLayout leaveGridLayout = new LeaveGridLayout(2, 4, 4, 6,
                                                LeaveGridLayout.HAlign.left,
                                                LeaveGridLayout.VAlign.center);
        leaveGridLayout.setColumnsHAlign(LeaveGridLayout.HAlign.right, LeaveGridLayout.HAlign.full);
        JPanel mainPanel = new JPanel(leaveGridLayout);

 * </pre>
 */
public class LeaveGridLayout extends GridLayout {

    private enum SizeType {
        min, pref;
    }

    /**
     * Horizontal aligment of component.
     */
    public static enum HAlign {
        left,
        right,
        center,
        full;
    }

    /**
     * Vertical aligment of component.
     */
    public static enum VAlign {
        top,
        bottom,
        center,
        full;
    }

    //----------------------------------------------------------------------

    private HAlign[] columsHAlign;
    private VAlign[] columsVAlign;

    //----------------------------------------------------------------------

    /**
     * Initialize layout manager with defaults.
     * <p>Hgap if 0, Vgap is 0, hAligment is left, vAligment is center.
     * @param columnsCount count of columns (greater than 0).
     * @param rowsCount count of rows (greater than 0).
     */
    public LeaveGridLayout(int columnsCount, int rowsCount) {
        this(columnsCount, rowsCount, 0, 0, HAlign.left, VAlign.center);
    }

    /**
     * Initialize layout manager.
     * @param columnsCount count of columns (greater than 0).
     * @param rowsCount count of rows (greater than 0).
     * @param hgap horizontal gap size. Gaps are placed between each of the columns.
     * @param vgap vertical gap size. Gaps are placed between each of the rows.
     * @param hAlign horizontal aligment of component in cell.
     * @param vAlign vertical aligment of component in cell.
     */
    public LeaveGridLayout(int columnsCount, int rowsCount, int hgap, int vgap,
                           HAlign hAlign, VAlign vAlign) {
        super(rowsCount, columnsCount, hgap, vgap);
        if(rowsCount < 1) throw new IllegalArgumentException("Count of rows can not be less than 1");
        if(columnsCount < 1) throw new IllegalArgumentException("Count of columns can not be less than 1");

        columsHAlign = new HAlign[columnsCount];
        for (int i = 0; i < columsHAlign.length; i++) {
            columsHAlign[i] = hAlign;
        }
        columsVAlign = new VAlign[rowsCount];
        for (int i = 0; i < columsVAlign.length; i++) {
            columsVAlign[i] = vAlign;
        }
    }

    //----------------------------------------------------------------------

    /**
     * Setup horizontal aligments of columns.
     * @param hAlign a list of aligments for columns from left to right.
     */
    public void setColumnsHAlign(HAlign... hAlign){
        for (int i = 0; i < hAlign.length && i < getColumns(); i++) {
            columsHAlign[i] = hAlign[i];
        }
    }

    /**
     * Setup vertical aligment of rows.
     * @param vAlign a lis of aligments for rows from top to bottom.
     */
    public void setRowsVAlign(VAlign... vAlign){
        for (int i = 0; i < vAlign.length && i < getRows(); i++) {
            columsVAlign[i] = vAlign[i];
        }
    }

    public void addLayoutComponent(String name, Component comp) {
        //do nothing
    }

    public void removeLayoutComponent(Component comp) {
        //do nothing
    }

    public Dimension preferredLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
            return layoutSize(parent, SizeType.pref).paneSize;
        }
    }

    public Dimension minimumLayoutSize(Container parent) {
        synchronized (parent.getTreeLock()) {
//            return layoutSize(parent, SizeType.min).paneSize;
            return layoutSize(parent, SizeType.pref).paneSize;
        }
    }

    public void layoutContainer(Container parent) {
        synchronized (parent.getTreeLock()) {
            Insets insets = parent.getInsets();
            int nComponents = parent.getComponentCount();
            int nRows = getRows();
            int nCols = getColumns();
            int nCells = nRows * nCols;

            if (nComponents == 0) {
                return;
            }

            LayoutResult layoutResult = layoutSize(parent, SizeType.pref);

            for (int i = 0; i < nComponents && i < nCells; i++) {
                Component comp = parent.getComponent(i);
                int compCol = i % nCols;
                int compRow = i / nCols;

                int compX = insets.left + compCol * getHgap();
                for (int j = 0; j < compCol; j++) {
                    compX += layoutResult.colsWidth[j];
                }
                if(columsHAlign[compCol] == HAlign.right){
                    int k = layoutResult.colsWidth[compCol] - comp.getPreferredSize().width;
                    if(k > 0){
                        compX += k;
                    }
                }else if(columsHAlign[compCol] == HAlign.center){
                    int k = layoutResult.colsWidth[compCol] - comp.getPreferredSize().width;
                    if(k > 0){
                        compX += k / 2;
                    }
                }

                int compY = insets.top + compRow * getVgap();
                for (int j = 0; j < compRow; j++) {
                    compY += layoutResult.rowsHeight[j];
                }
                if(columsVAlign[compRow] == VAlign.bottom){
                    int k = layoutResult.rowsHeight[compRow] - comp.getPreferredSize().height;
                    if(k > 0){
                        compY += k;
                    }
                }else if(columsVAlign[compRow] == VAlign.center){
                    int k = layoutResult.rowsHeight[compRow] - comp.getPreferredSize().height;
                    if(k > 0){
                        compY += k / 2;
                    }
                }

                int compWidth = comp.getPreferredSize().width;
                if(columsHAlign[compCol] == HAlign.full){
                    compWidth = layoutResult.colsWidth[compCol];
                }

                int compHeight = comp.getPreferredSize().height;
                if(columsVAlign[compRow] == VAlign.full){
                    compHeight = layoutResult.rowsHeight[compRow];
                }
                
                comp.setBounds(compX, compY, compWidth, compHeight);
            }
        }
    }

    //----------------------------------------------------------------------

    private LayoutResult layoutSize(Container parent, SizeType sizeType) {
        synchronized (parent.getTreeLock()) {
            Insets insets = parent.getInsets();
            int nComponents = parent.getComponentCount();
            int nRows = getRows();
            int nCols = getColumns();
            int nCells = nRows * nCols;

            int[] colsWidth = new int[nCols];
            int[] rowsHeight = new int[nRows];

            for (int i = 0; i < nCells && i < nComponents; i++) {
                Component comp = parent.getComponent(i);
                int compCol = i % nCols;
                int compRow = i / nCols;

                Dimension d = (sizeType == SizeType.min)
                              ? comp.getMinimumSize() : comp.getPreferredSize();

                if(colsWidth[compCol] < d.width){
                    colsWidth[compCol] = d.width;
                }
                if(rowsHeight[compRow] < d.height){
                    rowsHeight[compRow] = d.height;
                }
            }

            int sumWidth = 0;
            int sumHeight = 0;
            for (int i = 0; i < colsWidth.length; i++) {
                sumWidth += colsWidth[i];
            }
            for (int i = 0; i < rowsHeight.length; i++) {
                sumHeight += rowsHeight[i];
            }

            int paneWidth = insets.left + insets.right + sumWidth + (nCols - 1) * getHgap();
            int paneHeight = insets.top + insets.bottom + sumHeight + (nRows - 1) * getVgap();
            Dimension paneSize = new Dimension(paneWidth, paneHeight);

            return new LayoutResult(paneSize, colsWidth, rowsHeight);
        }
    }

    class LayoutResult {
        private Dimension paneSize;
        private int[] colsWidth;
        private int[] rowsHeight;

        LayoutResult(Dimension paneSize, int[] colsWidth, int[] rowsHeight) {
            this.paneSize = paneSize;
            this.colsWidth = colsWidth;
            this.rowsHeight = rowsHeight;
        }
    }

}
